package com.xrui.hbase;

import com.google.common.base.Objects;
import com.google.common.base.Preconditions;

/**
 * HBaseCell represents a cell in a HBase table.
 * <p>
 * It contains the family, qualifier, timestamp
 * that uniquely locates the cell within a table as well as the data in the cell.
 * <p>
 *
 * @param <T> Type of data stored in the cell.
 */
public final class HBaseCell<T> {
    private final EntityId mEntityId;
    private final ColumnName mColumn;
    private final long mTimestamp;
    private final DecodedCell<T> mDecodedCell;

    /**
     * Initializes a new HBaseCell.
     *
     * @param column      Name of the column in which this cell exists.
     * @param timestamp   Timestamp of this cell in milliseconds since the epoch.
     * @param decodedCell Decoded cell content.
     */
    private HBaseCell(
        final EntityId entityId,
        final ColumnName column,
        final long timestamp,
        final DecodedCell<T> decodedCell
    ) {
        mEntityId = entityId;
        mColumn = column;
        mTimestamp = timestamp;
        mDecodedCell = decodedCell;
    }

    /**
     * Initializes a HBaseCell.
     *
     * @param family      HBase column family name of the cell.
     * @param qualifier   HBase column qualifier name of the cell.
     * @param timestamp   Timestamp the cell was written at, in ms since the Epoch.
     * @param decodedCell Decoded cell content.
     * @deprecated HBaseCell constructor is deprecated.
     */
    @Deprecated
    public HBaseCell(EntityId entityId, String family, String qualifier, long timestamp, DecodedCell<T> decodedCell) {
        mEntityId = entityId;
        mColumn = ColumnName.create(Preconditions.checkNotNull(family), qualifier);
        mTimestamp = timestamp;
        mDecodedCell = Preconditions.checkNotNull(decodedCell);
    }

    /**
     * Create a new HBaseCell from the given coordinates and decoded value.
     *
     * @param entityId    entity ID.
     * @param column      Name of the column in which this cell exists.
     * @param timestamp   Timestamp of this cell in milliseconds since the epoch.
     * @param decodedCell Decoded cell content.
     * @param <T>         Type of the value of this cell.
     * @return a new HBaseCell from the given coordinates and decoded value.
     */
    public static <T> HBaseCell<T> create(
        final EntityId entityId,
        final ColumnName column,
        final long timestamp,
        final DecodedCell<T> decodedCell
    ) {
        return new HBaseCell<T>(entityId, column, timestamp, decodedCell);
    }

    public EntityId getEntityId() {
        return mEntityId;
    }

    /**
     * Get the name of the column in which this cell exists.
     *
     * @return the name of the column in which this cell exists.
     */
    public ColumnName getColumn() {
        return mColumn;
    }

    /**
     * @return the HBase column family name of this cell.
     * @deprecated getFamily is deprecated. Please use {@link #getColumn()}. This method will be
     * removed in the future.
     */
    @Deprecated
    public String getFamily() {
        return mColumn.getFamily();
    }

    /**
     * @return the HBase column qualifier name of this cell.
     * @deprecated getQualifier is deprecated. Please use {@link #getColumn()}. This method will be
     * removed in the future.
     */
    @Deprecated
    public String getQualifier() {
        return mColumn.getQualifier();
    }

    /**
     * Get the timestamp of this cell in milliseconds since the epoch.
     *
     * @return the timestamp of this cell in milliseconds since the epoch.
     */
    public long getTimestamp() {
        return mTimestamp;
    }

    /**
     * Get the content of this cell.
     *
     * @return the content of this cell.
     */
    public T getData() {
        return mDecodedCell.getData();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean equals(Object object) {
        if (object instanceof HBaseCell) {
            final HBaseCell<?> that = (HBaseCell<?>) object;
            return this.mEntityId.equals(that.mEntityId)
                && this.mColumn.equals(that.mColumn)
                && (this.mTimestamp == that.mTimestamp)
                && this.mDecodedCell.equals(that.mDecodedCell);
        } else {
            return false;
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int hashCode() {
        return Objects.hashCode(mEntityId, mColumn, mTimestamp, mDecodedCell);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        return Objects.toStringHelper(HBaseCell.class)
            .add("rowKey", mEntityId.getHBaseRowKey())
            .add("column", mColumn.getName())
            .add("timestamp", mTimestamp)
            .add("content", getData())
            .toString();
    }
}
